#include <QDesktopServices>
#include <QUrl>
#include "impresor.h"
#include "matematicas.h"
#include "grupoelementos.h"

Impresor::Impresor()
{    
    printer.setPaperSize(QPrinter::A4);
    printer.setOrientation(QPrinter::Portrait);    
    printer.setPageMargins(5.01, 5.01, 5.01, 5.01, QPrinter::Millimeter);

    paginaActual = 0;
    modoCalculoTamano = false;
    cabecera = &documentoVacio;
    pie = &documentoVacio;
}

Impresor::~Impresor()
{

}
void Impresor::activarSalidaImpresora()
{
    printer.setOutputFormat(QPrinter::NativeFormat);
    this->nombreArchivoPdf.clear();
}

void Impresor::activarSalidaPdf(const QString& nombreArchivoPdf, bool mostrarResultado)
{   
    printer.setOutputFormat(QPrinter::PdfFormat);
    printer.setOutputFileName(nombreArchivoPdf);
    printer.setResolution(1200);
    this->nombreArchivoPdf = mostrarResultado ? nombreArchivoPdf : "";
}

int Impresor::getDpi()
{
    int dpi = printer.resolution();
    return dpi;
}

QSizeF Impresor::getTamanoPaginaMilimetros()
{
    QRectF rectanguloPagina = printer.pageRect(QPrinter::Millimeter);
    return QSizeF(round(rectanguloPagina.width()), round(rectanguloPagina.height()));
}

QSize Impresor::getTamanoPaginaPixeles()
{
    QRect rectanguloPagina = printer.pageRect();
    return QSize(rectanguloPagina.width(), rectanguloPagina.height());
}

void Impresor::imprimir(Informe* informe)
{
    imprimir(informe->getCabecera(), informe->getCuerpo(), informe->getPie());
}

void Impresor::imprimir(FragmentoImpresion* cuerpo)
{
    imprimir(NULL, cuerpo, NULL);
}

void Impresor::imprimir(FragmentoImpresion* cabecera, FragmentoImpresion* cuerpo, FragmentoImpresion* pie)
{      
    if (cabecera == NULL) {
        cabecera = &documentoVacio;
    }
    if (cuerpo == NULL) {
        cuerpo = &documentoVacio;
    }
    if (pie == NULL) {
        pie = &documentoVacio;
    }

    this->cabecera = cabecera;
    this->pie = pie;

    QPainter painter;
    painter.begin(&printer);

    int altoPagina = getTamanoPaginaPixeles().height();
    int anchoPagina = getTamanoPaginaPixeles().width();
    int altoCabecera = calcularAlto(cabecera, &painter);
    int altoPie = calcularAlto(pie, &painter);
    int altoCuerpo = altoPagina - altoCabecera - altoPie;
    
    QRect rectanguloCabecera(QPoint(0, 0), QSize(anchoPagina, altoCabecera));
    estadoImpresionCabecera.setPainter(&painter);
    estadoImpresionCabecera.setRectanguloImpresion(rectanguloCabecera);

    QRect rectanguloCuerpo(QPoint(0, altoCabecera), QSize(anchoPagina, altoPagina - altoCabecera - altoPie));
    rectanguloCuerpo.setHeight(altoCuerpo);    
    estadoImpresionCuerpo.setPainter(&painter);
    estadoImpresionCuerpo.setRectanguloImpresion(rectanguloCuerpo);
    estadoImpresionCuerpo.inicializar();
    
    QRect rectaguloPie(QPoint(0, altoCabecera + altoCuerpo), QSize(anchoPagina, altoPie));
    estadoImpresionPie.setPainter(&painter);
    estadoImpresionPie.setRectanguloImpresion(rectaguloPie);

    if (altoCabecera + altoPie > altoPagina / 2) {
        // TODO: informar del error
        return;
    }

    modoCalculoTamano = false;

    paginaActual = 1;
    imprimirPaginas(LectorDocumento(cuerpo), estadoImpresionCuerpo, true);

    painter.end();

    if (!nombreArchivoPdf.isEmpty()) {
        QDesktopServices::openUrl(QUrl("file:///" + nombreArchivoPdf, QUrl::TolerantMode));
    }
}

int Impresor::calcularAlto(FragmentoImpresion* documento, QPainter* painter)
{
    modoCalculoTamano = true;
    EstadoImpresion estado(painter, QRect(QPoint(0, 0), getTamanoPaginaPixeles()));
    imprimirPaginas(LectorDocumento(documento), estado, false);

    return estado.getDesplazamientoYPixeles();
}

void Impresor::imprimirPaginas(LectorDocumento& lector, EstadoImpresion& estado, bool imprimiendoCuerpo)
{   
    //if (imprimiendoCuerpo) {
    //    estado.recuadrar(Qt::red);
    //}

    // Valores en pixeles
    int anchoColumnaActual = 0; 
    int anchoLineaActual = 0;
    int altoLineaActual = 0;
    
    if (imprimiendoCuerpo) {
        inicioPagina();
    }
       
    QList<ElementoImpresion*> elementosLinea;
    while (lector.quedanElementos()) {

        ElementoImpresion* elemento = lector.obtenerSiguienteElemento();

        QSize sizeElemento = elemento->obtenerDimensiones(estado);
        int anchoElemento = sizeElemento.width();
        bool hayQueImprimirLinea =  elemento->saltoLinea() || elemento->saltoPagina() || !lector.quedanElementos();
        bool hayQueDividirLinea = estado.enModoParrafo() &&
            anchoElemento + anchoLineaActual > estado.getRectanguloImpresion().width();

        if (hayQueDividirLinea && !elemento->permiteDivision() && !elementosLinea.isEmpty()) {
            lector.devolverElemento(elemento);
            hayQueImprimirLinea = true;
        } else if (hayQueDividirLinea && elemento->permiteDivision()) {
            int anchoDisponible = estado.getAnchoPaginaPixeles() - anchoLineaActual;
            ElementoImpresion* elementoAjustado = dividirElemento(lector, elemento, anchoDisponible, estado);        
            elementosLinea.append(elementoAjustado);
            anchoLineaActual += elementoAjustado->obtenerDimensiones(estado).width();
            hayQueImprimirLinea = true;
        } else {
            elementosLinea.append(elemento);
            anchoLineaActual += anchoElemento;
            if (anchoLineaActual >= estado.getAnchoPaginaPixeles()) {
                hayQueImprimirLinea = true;
            }
        }

        if (!hayQueImprimirLinea) {
            continue;
        }

        altoLineaActual = calcularAltoLinea(elementosLinea, estado);
        int altoDisponible = estado.getRectanguloImpresion().height();

        bool provocarSaltoPagina = elemento->saltoPagina() ||
            estado.getDesplazamientoYPixeles() + altoLineaActual > altoDisponible;

        if (provocarSaltoPagina && !imprimiendoCuerpo) {
            return;
        }

        //TODO: Ver si no cabe en una pagina vacia
        if (provocarSaltoPagina) {
            finPagina();
            inicioPagina();        
        }

        if (estado.enModoLinea() || estado.enModoTabla()) {
            anchoLineaActual = estado.getAnchoPaginaPixeles(); // Esto evita que se centren las columnas
        }
        imprimirLineaElementos(elementosLinea, estado, anchoLineaActual);

        int incrementoYPixeles = altoLineaActual + (estado.getAltoPixelesFuente() * (estado.getInterlineado() - 1));        
        
        estado.incrementarDesplazamientoYPixeles(incrementoYPixeles);
        elementosLinea.clear();
        anchoLineaActual = 0;
    }

    if (imprimiendoCuerpo) {
        finPagina();
    }
}

void Impresor::imprimirLineaElementos(QList<ElementoImpresion*> elementosLinea, EstadoImpresion& estado, int /*anchoLinea*/)
{
    QRect rectanguloLinea(estado.getRectanguloImpresion());
    rectanguloLinea.setY(rectanguloLinea.y() + estado.getDesplazamientoYPixeles());
    rectanguloLinea.setHeight(calcularAltoLinea(elementosLinea, estado));

    if (!modoCalculoTamano) {
        GrupoElementos grupoElementos;
        grupoElementos.agregar(elementosLinea);
        grupoElementos.imprimir(estado, rectanguloLinea);
    }
}

int Impresor::calcularAltoLinea(QList<ElementoImpresion*> elementosLinea, EstadoImpresion& estado)
{
    int altoMaximo = estado.getAltoPixelesFuente();
    foreach(ElementoImpresion* elemento, elementosLinea)
    {
        int altoElemento = elemento->obtenerDimensiones(estado).height();
        altoMaximo = altoMaximo > altoElemento ? altoMaximo : altoElemento;
    }

    return altoMaximo;
}

void Impresor::inicioPagina()
{
    if (paginaActual > 1) {
        printer.newPage();
    }

    estadoImpresionCabecera.inicializar();
    estadoImpresionCuerpo.setDesplazamientoYPixeles(0);
    estadoImpresionPie.inicializar();

    estadoImpresionCabecera.setNumeroPagina(paginaActual);
    estadoImpresionCuerpo.setNumeroPagina(paginaActual);
    estadoImpresionPie.setNumeroPagina(paginaActual);

    imprimirPaginas(LectorDocumento(cabecera), estadoImpresionCabecera, false);
}

void Impresor::finPagina()
{
    imprimirPaginas(LectorDocumento(pie), estadoImpresionPie, false);     

    paginaActual++;
}

ElementoImpresion* Impresor::dividirElemento(LectorDocumento& lector, ElementoImpresion* elemento, int anchoDisponible, EstadoImpresion& estado)
{
    ElementoImpresion* elementoRestante = NULL;

    ElementoImpresion* elementoAjustado = elemento->dividir(anchoDisponible, estado, elementoRestante);

    if (elementoRestante != NULL) {
        lector.devolverElemento(elementoRestante);
    }

    return elementoAjustado;
}

int Impresor::calcularDescenso(QList<ElementoImpresion*> elementosLinea, EstadoImpresion& estado)
{
    int descensoMaximo = 0;

    foreach(ElementoImpresion* elemento, elementosLinea)
    {
        int descensoElemento = elemento->obtenerDescenso(estado);
        descensoMaximo = descensoMaximo > descensoElemento ? descensoMaximo : descensoElemento;
    }

    return descensoMaximo;
}